package com.tuoyu.peopletest.utils;

import android.annotation.TargetApi;
import android.net.Uri;
import android.os.Build;

import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class WebApi {
    private static final int CORE_POOL_SIZE = 5;
    private static HttpThreadPool mThreadPool;
    private String host;
    private Integer timeout;

    public WebApi() {
        this.host = "";
    }

    public WebApi(int timeout) {
        this.host = "";
        this.timeout = timeout;
    }

    public WebApi(String host) {
        this.host = host;
    }

    public WebApi(String host, int timeout) {
        this.host = host;
        this.timeout = timeout;
    }

    public void get(String url, HttpListener cb) {
        this.get(url, null, null, cb);
    }

    public void get(String url, Map<String, String> params, HttpListener cb) {
        this.get(url, params, null, cb);
    }

    public void get(String url, Map<String, String> params, Map<String, String> header, HttpListener cb) {
        Request req = new Request(getRawUrl(url, params), "GET");
        req.appHeader(header);
        req.setListener(cb);
        if (this.timeout != null) {
            req.setTimeout(this.timeout);
        }
        ThreadPoolExecutor threadPoolExecutor = getThreadExecutor();
        threadPoolExecutor.execute(req);
    }

    public void post(String url, Map<String, Object> body, HttpListener cb) {
        this.post(url, null, body, null, cb);
    }

    public void post(String url, String body, HttpListener cb) {
        this.post(url, null, body, null, cb);
    }

    public void post(String url, Map<String, String> params, Map<String, Object> body, HttpListener cb) {
        this.post(url,params, body, null, cb);
    }

    public void post(String url, Map<String, String> params, String body, HttpListener cb) {
        this.post(url,params, body, null, cb);
    }

    public void post(String url, Map<String, String> params, Map<String, Object> body, Map<String, String> header, HttpListener cb) {
        Request req = new Request(getRawUrl(url, params), "POST");
        req.appHeader(header);
        req.setListener(cb);
        req.setBody(body);
        if (this.timeout != null) {
            req.setTimeout(this.timeout);
        }
        ThreadPoolExecutor threadPoolExecutor = getThreadExecutor();
        threadPoolExecutor.execute(req);
    }

    public void post(String url, Map<String, String> params, String body, Map<String, String> header, HttpListener cb) {
        Request req = new Request(getRawUrl(url, params), "POST");
        req.appHeader(header);
        req.setListener(cb);
        req.setBody(body);
        if (this.timeout != null) {
            req.setTimeout(this.timeout);
        }
        ThreadPoolExecutor threadPoolExecutor = getThreadExecutor();
        threadPoolExecutor.execute(req);
    }

    private String getRawUrl(String url, Map<String, String> params) {
        if (url == null) { url = ""; }
        String urlLowerCase = url.toLowerCase();
        String result = this.host;
        if (result == null || result.isEmpty() || urlLowerCase.startsWith("http://") || urlLowerCase.startsWith("https://")) {
            result = url;
        } else if (url.length() > 0) {
            if (result.endsWith("/")) {
                if (url.startsWith("/")) {
                    result = result + url.substring(0, url.length() - 1);
                } else {
                    result = result + url;
                }
            } else {
                if (url.startsWith("/")) {
                    result = result + url;
                } else {
                    result = result + "/" + url;
                }
            }
        }
        if (params != null && params.size() > 0) {
            if (result.indexOf("?") < 0) {
                result = result + "?";
            } else {
                result = result + "&";
            }
            for (Map.Entry<String, String> item: params.entrySet()) {
                result = result + Uri.encode(item.getKey()) + "=" + Uri.encode(item.getValue());
            }
        }
        return result;
    }

    private synchronized ThreadPoolExecutor getThreadExecutor() {
        if (mThreadPool == null || mThreadPool.isShutdown()) {
            mThreadPool = new HttpThreadPool(CORE_POOL_SIZE);
        }
        return mThreadPool;
    }

    public static class HttpThreadPool extends ThreadPoolExecutor {
        @TargetApi(Build.VERSION_CODES.GINGERBREAD)
        public HttpThreadPool(int poolSize) {
            super(poolSize, poolSize, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<Runnable>(),
                    Executors.defaultThreadFactory(), new AbortPolicy());
        }
    }

    public static abstract class HttpListener {
        public boolean onResponse(String response) {
            return false;
        }
        public abstract void onSuccess(JSONObject response);
        public abstract void onFailed(String message, int status);
    }

    private static class Request implements Runnable {
        private String url;
        private String method;
        private int timeout = 30000;
        private String body;
        private HttpListener listener;
        private Map<String, String> header = new HashMap<String, String>();

        public Request(String url, String method) {
            this.url = url;
            this.method = method;
            this.header.put("Accept", "application/json");
            this.header.put("Charset", "utf-8");
            this.header.put("Content-Type", "application/json");
        }

        public Request setTimeout(int value) {
            this.timeout = value;
            return this;
        }

        public Request setBody(Map<String, Object> body) {
            this.body = body.toString();
            return this;
        }

        public Request setBody(String body) {
            this.body = body;
            return this;
        }

        public Request setListener(HttpListener listener) {
            this.listener = listener;
            return this;
        }

        public Request setHeader(String key, String value) {
            if (this.header.containsKey(key)) {
                this.header.remove(key);
            }
            this.header.put(key, value);
            return this;
        }

        public Request appHeader(Map<String, String> header) {
            if (header != null) {
                for (Map.Entry<String, String> item: header.entrySet()) {
                    this.setHeader(item.getKey(), item.getValue());
                }
            }
            return this;
        }

        @Override
        public void run() {
            request();
        }

        private void request() {
            InputStream inputStream = null;
            BufferedReader br = null;
            try {
                HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
                conn.setRequestMethod(this.method);
                conn.setConnectTimeout(this.timeout);
                conn.setReadTimeout(this.timeout);
                conn.setUseCaches(false);
                if (this.header != null) {
                    for (Map.Entry<String, String> item: this.header.entrySet()) {
                        conn.setRequestProperty(item.getKey(), item.getValue());
                    }
                }
                if (this.method.compareToIgnoreCase("post")==0) {
                    byte[] data = (this.body+"\r\n").getBytes("utf-8");
                    conn.setRequestProperty("Content-Length", String.valueOf(data.length));
                    conn.getOutputStream().write(data);
                }
                int respCode = conn.getResponseCode();
                if (respCode == 200) {
                    StringBuilder sb = new StringBuilder();
                    inputStream = conn.getInputStream();
                    br = new BufferedReader(new InputStreamReader(inputStream, "utf-8"));
                    String readLine;
                    while ((readLine = br.readLine()) != null) {
                        sb.append(readLine);
                    }
                    if (sb.length() > 0 && listener != null) {
                        String data = sb.toString();
                        if (!listener.onResponse(data)) {
                            JSONObject json = new JSONObject(data);
                            listener.onSuccess(json);
                        }
                    }
                } else {
                    if (listener != null) {
                        listener.onFailed(conn.getResponseMessage(), respCode);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                if (listener != null) {
                    listener.onFailed(e.getMessage(), -1);
                }
            } finally {
                try {
                    if (inputStream != null) inputStream.close();
                    if (br != null) br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


}
